/*
The code examples copyrighted by Nokia Corporation that are included to
this material are licensed to you under following MIT-style License:

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include <iostream>
#include <iomanip>

#include <wlancond.h>
#include <wlancond-dbus.h>

#include "DbusHandler.hpp"



DbusHandler::DbusHandler() :
		iface(WLANCOND_SERVICE, WLANCOND_REQ_PATH, WLANCOND_REQ_INTERFACE, QDBusConnection::systemBus())
{
	if(!this->iface.isValid()){
		std::cerr << "Could not create D-Bus interface." << std::endl;
		throw std::exception();
	}

	QDBusConnection::systemBus().connect(
			WLANCOND_SERVICE,
			WLANCOND_SIG_PATH,
			WLANCOND_SIG_INTERFACE,
			WLANCOND_SCAN_RESULTS_SIG,
			this,
			SLOT(ScanResult(const QDBusMessage&))
		);
}



//Translate encryption type bit code to readable string describing
//the encryption type.
static std::string EncryptionBitsToString(unsigned bits){
	switch(bits & WLANCOND_ENCRYPT_METHOD_MASK){
		default:
		case WLANCOND_OPEN:
			return "NONE";
			break;
		case WLANCOND_WEP:
			return "WEP";
			break;
		case WLANCOND_WPA_PSK:
			return "WPA_PSK";
			break;
		case WLANCOND_WPA_EAP:
			return "WPA_EAP";
			break;
	}
}



//Slot to receive a result of scan for wireless networks request, from wlancond daemon.
void DbusHandler::ScanResult(const QDBusMessage& reply){
	QList<QVariant> l = reply.arguments();

	for(int i = 1; i < l.size();){
		QVariant essid = l.at(i);
		++i;
		std::cout << "ESSID: " << essid.toString().toUtf8().constData() << std::endl;

		QVariant bssid = l.at(i);
		++i;
		{
			QByteArray a = bssid.toByteArray();
			std::cout << "Address: "
					<< std::setw(2) << std::setfill('0') << std::hex << int(a.constData()[0]) << ":"
					<< std::setw(2) << std::setfill('0') << std::hex << int(a.constData()[1]) << ":"
					<< std::setw(2) << std::setfill('0') << std::hex << int(a.constData()[2]) << ":"
					<< std::setw(2) << std::setfill('0') << std::hex << int(a.constData()[3]) << ":"
					<< std::setw(2) << std::setfill('0') << std::hex << int(a.constData()[4]) << ":"
					<< std::setw(2) << std::setfill('0') << std::hex << int(a.constData()[5])
					<< std::endl;
		}

		QVariant rssi = l.at(i);
		++i;
		std::cout << "Signal level: " << std::dec << rssi.toInt() << std::endl;

		QVariant channel = l.at(i);
		++i;
		std::cout << "Channel: " << std::dec << channel.toUInt() << std::endl;

		QVariant capbits = l.at(i);
		++i;
		std::cout << "Capbits: " << "0x" << std::hex << capbits.toUInt() << std::endl;

		std::cout << "Encryption: " << EncryptionBitsToString(capbits.toUInt()) << std::endl;

		std::cout << std::endl;
	}

	QCoreApplication::instance()->quit();
}



//Request wlancond daemon to scan for available wireless networks.
//The result containing list of available wireless networks will be
//delivered to ScanResult() slot.
void DbusHandler::Scan(){
	std::cout << "Scanning..." << std::endl;
	this->iface.call(
			WLANCOND_SCAN_REQ,
			QVariant(int(WLANCOND_TX_POWER100)),
			QString("").toUtf8(),
			QVariant(unsigned(0))
		);
}



//Slot to receive a result of connect to wireless network request,
//sent to wlancond daemon.
void DbusHandler::ConnectResult(const QDBusMessage& reply){
	std::cout << "Connect result" << std::endl;
	QList<QVariant> l = reply.arguments();

	for(int i = 0; i < l.size(); ++i){
		std::cout << l[i].toString().toUtf8().constData() << std::endl;
	}

	QCoreApplication::instance()->quit();
}



//Send a connect to wireless network request to wlancond daemon.
//The result will be delivered to ConnectResult() slot.
void DbusHandler::Connect(QString ssid, QString encryption, QString key){
	std::cout << "Connecting..." << std::endl;

	QList<QVariant> args;

	args << QVariant(int(WLANCOND_TX_POWER100)); // power level

	//The SSID should include the terminating 0.
	args << ssid.toUtf8().append(char(0)); // SSID

	int encryptionType = int(WLANCOND_OPEN);
	if(encryption == "open"){
		encryptionType = int(WLANCOND_OPEN);
	}else if(encryption == "wep"){
		encryptionType = int(WLANCOND_WEP);
	}else if(encryption == "wpapsk"){
		encryptionType = int(WLANCOND_WPA_PSK);
	}else if(encryption == "wpaeap"){
		encryptionType = int(WLANCOND_WPA_EAP);
	}

	args << QVariant(int(WLANCOND_INFRA)); //mode
	args << QVariant(encryptionType); //encryption
	args << key.toUtf8(); //key to be used with encryption (password)
	args << QByteArray(); //dummy
	args << QByteArray(); //dummy
	args << QByteArray(); //dummy
	args << QVariant(int(1)); //default key
	args << QVariant(unsigned(0)); //adhoc_channel

	this->iface.callWithCallback(
			WLANCOND_SETTINGS_AND_CONNECT_REQ,
			args,
			this,
			SLOT(ConnectResult(const QDBusMessage&))
		);
}



//Slot to receive a result of Disconnect request from wlancond daemon.
void DbusHandler::DisconnectResult(const QDBusMessage& reply){
	std::cout << "Disconnected" << std::endl;

	QList<QVariant> l = reply.arguments();

	for(int i = 0; i < l.size(); ++i){
		std::cout << l[i].toString().toUtf8().constData() << std::endl;
	}

	QCoreApplication::instance()->quit();
}



//Request disconnecting from wireless network.
//The request is sent to wlancond over dbus.
//The result will be delivered to DisconnectResult() slot.
void DbusHandler::Disconnect(){
	std::cout << "Disconnecting..." << std::endl;

	QList<QVariant> args;

	this->iface.callWithCallback(
			WLANCOND_DISCONNECT_REQ,
			args,
			this,
			SLOT(DisconnectResult(const QDBusMessage&))
		);
}



//Slot to receive result of currently connected network status request.
void DbusHandler::StatusResult(const QDBusMessage& reply){
	QList<QVariant> l = reply.arguments();

	std::cout << "ESSID: " << l[0].toString().toUtf8().constData() << std::endl;

	{
		QByteArray a = l[1].toByteArray();
		std::cout << "Address: "
				<< std::setw(2) << std::setfill('0') << std::hex << int(a.constData()[0]) << ":"
				<< std::setw(2) << std::setfill('0') << std::hex << int(a.constData()[1]) << ":"
				<< std::setw(2) << std::setfill('0') << std::hex << int(a.constData()[2]) << ":"
				<< std::setw(2) << std::setfill('0') << std::hex << int(a.constData()[3]) << ":"
				<< std::setw(2) << std::setfill('0') << std::hex << int(a.constData()[4]) << ":"
				<< std::setw(2) << std::setfill('0') << std::hex << int(a.constData()[5])
				<< std::endl;
	}

	std::cout << "Signal level: " << std::dec << l[2].toInt() << std::endl;

	std::cout << "Channel: " << std::dec << l[3].toUInt() << std::endl;

	std::cout << "Capbits: " << "0x" << std::hex << l[4].toUInt() << std::endl;

	std::cout << "Encryption: " << EncryptionBitsToString(l[5].toUInt()) << std::endl;

	std::cout << "Interface: " << l[6].toString().toUtf8().constData() << std::endl;

	QCoreApplication::instance()->quit();
}



//Request status of currently connected network from wlancond daemon.
//The result will be delivered to StatusResult() slot.
void DbusHandler::Status(){
	QList<QVariant> args;

	this->iface.callWithCallback(
			WLANCOND_STATUS_REQ,
			args,
			this,
			SLOT(StatusResult(const QDBusMessage&))
		);
}



//Slot to receive a result of connection status request.
void DbusHandler::ConnectionStatusResult(const QDBusMessage& reply){
	QList<QVariant> l = reply.arguments();

	if(l.size() >= 1){
		if(l[0].toBool()){
			std::cout << "Connected" << std::endl;
		}else{
			std::cout << "Disconnected" << std::endl;
		}
	}else{
		std::cout << "Error" << std::endl;
	}

	QCoreApplication::instance()->quit();
}



//Request connection status from wlancond daemon.
//The result will be delivered to ConnectionStatusResult() slot.
void DbusHandler::ConnectionStatus(){
	QList<QVariant> args;

	this->iface.callWithCallback(
			WLANCOND_CONNECTION_STATUS_REQ,
			args,
			this,
			SLOT(ConnectionStatusResult(const QDBusMessage&))
		);
}
